//! 线性延迟退休政策.
//! 即每迟生`x`个月, 就要延迟退休`x/k`个月.
//!
//! json配置支持对一个性别的不同年龄的策略进行分别配置(每个年龄的配置见`PolicyConfigOneGenderItem`).
//! 实际上还很容易扩展出新的性别(笑).

use std::{cmp::min, collections::HashMap};

use super::*;
use serde::Deserialize;

/// 线性延迟退休政策
pub struct LinearDelayPolicy {
    policy_config: HashMap<Gender, PolicyConfigGender>,
}

impl LinearDelayPolicy {
    /// 从策略json生成
    pub fn with_json(json_path: &str) -> anyhow::Result<Self> {
        let policy_config: HashMap<Gender, PolicyConfigGender> = tools::read_json_file(json_path)?;
        // println!("json_result: \n{:#?}", json_result);
        println!("policy_config: \n{:#?}", policy_config);
        Ok(Self { policy_config })
    }

    /// 定位到对应的policy config.
    /// 无论是性别还是年龄, 提供了就定位到符合的, 没提供就取默认的第一个.
    ///
    /// 在指定目标却无法找到, 或范围内没有任何目标的时候返回`Err`.
    fn determinate_policy(
        &self,
        gender: Option<&Gender>,
        prev_retire_age: Option<&u32>,
    ) -> anyhow::Result<PolicyContext> {
        let res_gender;
        let res_prev_retire_age;

        let gender_config = if let Some(gender) = gender {
            res_gender = gender;
            self.policy_config
                .get(gender)
                .ok_or(anyhow::anyhow!("No matched gender policy"))?
        } else {
            // 默认使用第一个gender
            let first_config = self.policy_config.iter().next();
            if let Some(t) = first_config {
                res_gender = t.0;
                t.1
            } else {
                return Err(anyhow::anyhow!("No any available gender policy"));
            }
        };

        let age_config = if let Some(prev_retire_age) = prev_retire_age {
            res_prev_retire_age = prev_retire_age;
            gender_config
                .config_map
                .get(prev_retire_age)
                .ok_or(anyhow::anyhow!("No matched previous retire age policy"))?
        } else {
            // 默认使用第一个age
            let first_config = gender_config.config_map.iter().next();
            if let Some(t) = first_config {
                res_prev_retire_age = t.0;
                t.1
            } else {
                return Err(anyhow::anyhow!(
                    "No any available previous retire age policy"
                ));
            }
        };

        Ok(PolicyContext {
            gender: res_gender.clone(),
            prev_retire_age: res_prev_retire_age.clone(),
            config: age_config,
        })
    }

    /// 计算延迟退休的月数
    fn calculate_delay_months(
        birth_time: &(u32, u32),
        policy_config: &PolicyConfigGenderAge,
    ) -> u32 {
        let delta_months = (birth_time.0 as i32 - policy_config.begin_birth_year as i32) * 12
            + birth_time.1 as i32
            - 1;
        // 早于计算开始的时间出生
        if delta_months < 0 {
            return 0;
        }

        let delay_months = delta_months as u32 / policy_config.delay_factor + 1;
        min(delay_months, policy_config.max_delay_months)
    }
}

impl RetirePolicy for LinearDelayPolicy {
    fn get_retire_result(&self, msg: PersonMessage) -> anyhow::Result<RetireResult> {
        println!("INFO: Use linear delay retire policy.");

        let context = self.determinate_policy(msg.gender.as_ref(), msg.prev_retire_age.as_ref())?;
        println!("Chosen policy context: \n{:#?}", context);
        let delayed_retire_months = Self::calculate_delay_months(&msg.birth_time, &context.config);
        Ok(tools::calculate_retire_time(
            msg.birth_time,
            context.prev_retire_age * 12,
            delayed_retire_months as i32,
        ))
    }
}

/// 记录所有目标Policy的相关信息.
#[derive(Debug)]
#[allow(dead_code)]
struct PolicyContext<'c> {
    gender: Gender,
    prev_retire_age: u32,
    config: &'c PolicyConfigGenderAge,
}

/// 一个性别的策略配置
#[derive(Debug, Deserialize)]
struct PolicyConfigGender {
    /// 退休年龄 -> 策略
    #[serde(flatten)]
    #[serde(deserialize_with = "tools::deserialize_special_key_map")]
    config_map: HashMap<u32, PolicyConfigGenderAge>,
}

/// 一个性别的一个退休年龄的策略配置.
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct PolicyConfigGenderAge {
    /// 延迟退休开始计算的出生年
    begin_birth_year: u32,
    /// 延迟系数, 即 晚生`x`个月 -> 晚退休`x/delay_factor`个月
    delay_factor: u32,
    /// 最大延迟退休月数
    max_delay_months: u32,
}
